#pragma once

#include "Sock.hpp"
#include "Epoll.hpp"
#include <unistd.h>

static const int maxevents = 64;

class EpollServer
{
public:
    EpollServer(uint16_t port = 8080, const std::string& ip = "")
        : _listenSock(-1), _epfd(-1), _port(port)
        , _ip(ip), _ptr_events(nullptr)
    {
        // 1.获取监听套接字
        _listenSock = Sock::Socket();
        if(_listenSock < 0) exit(1);

        // 2.绑定端口号
        if(Sock::Bind(_listenSock, _ip, _port) < 0) exit(2);

        // 3.设置监听状态
        if(Sock::Listen(_listenSock) < 0) exit(3);

        // 4.创建epoll模型
        _epfd = Epoll::EpollCreate();
        if(_epfd < 0) exit(4);

        // 5.为_events开辟内存空间并进行初始化
        _ptr_events = new epoll_event[maxevents];

        // 6.将listenSock添加到epoll模型中
        if(Epoll::EpollCtl(_epfd, EPOLL_CTL_ADD, _listenSock, EPOLLIN) < 0) exit(5);

        logMessage(NORMAL, "EpollServer init success!\n");
    }

    // 启动运行函数
    void Start()
    {
        while(1)
        {
            // 对epoll进行等待
            int n = Epoll::EpollWait(_epfd, _ptr_events, maxevents, -1);

            switch(n)
            {
            case 0:
                logMessage(DEBUG, "epoll wait time out!\n");
                break;
            case -1:
                logMessage(ERROR, "epoll wait error, %d:%s\n", errno, strerror(errno));
                break;
            default:
                Handler(n);
                break;
            }
        }
    }

    // 析构函数
    ~EpollServer()
    {
        if(_listenSock >= 0) close(_listenSock);
        if(_epfd >= 0) close(_epfd);
        if(_ptr_events) delete[] _ptr_events;
    }

private:
    void Handler(int n)
    {
        // 遍历_events，查找已经就绪的事件
        for(int i = 0; i < n; ++i)
        {
            // 分listen套接字和普通套接字两种情况讨论
            if(_ptr_events[i].data.fd == _listenSock) Accepter(_listenSock);
            else Reciever(_ptr_events[i].data.fd);
        }
    }

    // 连接接收函数
    void Accepter(int listenSock)
    {
        std::string cli_ip;   // 发起连接的客户端ip
        uint16_t cli_port;    // 客户端端口号
        int fd = Sock::Accept(listenSock, cli_ip, cli_port);
        if(fd < 0) return;

        // 将新增的fd添加到epoll模型中去
        Epoll::EpollCtl(_epfd, EPOLL_CTL_ADD, fd, EPOLLIN);
        logMessage(NORMAL, "Add a new fd to epoll success, fd:%d\n", fd);
    }

    // 信息读取函数
    void Reciever(int fd)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, 1023);

        // 如果读取成功
        if(n > 0)
        {
            buffer[n - 1] = '\0';
            printf("Client# %s\n", buffer);
        }
        else if(n == 0)
        {
            // 对端关闭，将fd从epoll模型中拿走
            Epoll::EpollCtl(_epfd, EPOLL_CTL_DEL, fd, EPOLLIN);
            if(n == 0)
            {
                // logMessage(NORMAL, "Remove fd from epoll success, fd:%d\n", fd);
                printf("Remove fd from epoll success, fd:%d\n", fd);
                close(fd);
            }
        }
        else  // 读取数据失败
        {
            logMessage(ERROR, "Read message fail, %d:%s\n", errno, strerror(errno));
        }
    }

    int _listenSock;   // 监听套接字
    int _epfd;         // epoll套接字
    uint16_t _port;    // 服务进程端口号
    std::string _ip;   // 服务器ip 
    struct epoll_event* _ptr_events;   // 指向就绪队列的指针
};